<template>
	<view>
		<view ref="uni-rate" class="uni-rate">
			<view
				class="uni-rate__icon"
				:class="{ 'uni-cursor-not-allowed': disabled }"
				:style="{ 'margin-right': marginNumber + 'px' }"
				v-for="(star, index) in stars"
				:key="index"
				@touchstart.stop="touchstart"
				@touchmove.stop="touchmove"
				@mousedown.stop="mousedown"
				@mousemove.stop="mousemove"
				@mouseleave="mouseleave"
			>
				<uni-icons
					:color="color"
					:size="size"
					:type="isFill ? 'star-filled' : 'star'"
				/>
				<!-- #ifdef APP-NVUE -->
				<view
					:style="{
						width:
							(star.activeWitch.replace('%', '') * size) / 100 +
							'px',
					}"
					class="uni-rate__icon-on"
				>
					<uni-icons
						style="text-align: left"
						:color="disabled ? '#ccc' : activeColor"
						:size="size"
						type="star-filled"
					/>
				</view>
				<!-- #endif -->
				<!-- #ifndef APP-NVUE -->
				<view
					:style="{ width: star.activeWitch }"
					class="uni-rate__icon-on"
				>
					<uni-icons
						:color="disabled ? disabledColor : activeColor"
						:size="size"
						type="star-filled"
					/>
				</view>
				<!-- #endif -->
			</view>
		</view>
	</view>
</template>

<script>
// #ifdef APP-NVUE
const dom = uni.requireNativePlugin('dom')
// #endif
/**
 * Rate 评分
 * @description 评分组件
 * @tutorial https://ext.dcloud.net.cn/plugin?id=33
 * @property {Boolean} 	isFill = [true|false] 		星星的类型，是否为实心类型, 默认为实心
 * @property {String} 	color 						未选中状态的星星颜色，默认为 "#ececec"
 * @property {String} 	activeColor 				选中状态的星星颜色，默认为 "#ffca3e"
 * @property {String} 	disabledColor 				禁用状态的星星颜色，默认为 "#c0c0c0"
 * @property {Number} 	size 						星星的大小
 * @property {Number} 	value/v-model 				当前评分
 * @property {Number} 	max 						最大评分评分数量，目前一分一颗星
 * @property {Number} 	margin 						星星的间距，单位 px
 * @property {Boolean} 	disabled = [true|false] 	是否为禁用状态，默认为 false
 * @property {Boolean} 	readonly = [true|false] 	是否为只读状态，默认为 false
 * @property {Boolean} 	allowHalf = [true|false] 	是否实现半星，默认为 false
 * @property {Boolean} 	touchable = [true|false] 	是否支持滑动手势，默认为 true
 * @event {Function} change 						uniRate 的 value 改变时触发事件，e={value:Number}
 */

export default {
	name: 'UniRate',
	props: {
		isFill: {
			// 星星的类型，是否镂空
			type: [Boolean, String],
			default: true,
		},
		color: {
			// 星星未选中的颜色
			type: String,
			default: '#ececec',
		},
		activeColor: {
			// 星星选中状态颜色
			type: String,
			default: '#ffca3e',
		},
		disabledColor: {
			// 星星禁用状态颜色
			type: String,
			default: '#c0c0c0',
		},
		size: {
			// 星星的大小
			type: [Number, String],
			default: 24,
		},
		value: {
			// 当前评分
			type: [Number, String],
			default: 0,
		},
		modelValue: {
			// 当前评分
			type: [Number, String],
			default: 0,
		},
		max: {
			// 最大评分
			type: [Number, String],
			default: 5,
		},
		margin: {
			// 星星的间距
			type: [Number, String],
			default: 0,
		},
		disabled: {
			// 是否可点击
			type: [Boolean, String],
			default: false,
		},
		readonly: {
			// 是否只读
			type: [Boolean, String],
			default: false,
		},
		allowHalf: {
			// 是否显示半星
			type: [Boolean, String],
			default: false,
		},
		touchable: {
			// 是否支持滑动手势
			type: [Boolean, String],
			default: true,
		},
	},
	data() {
		return {
			valueSync: '',
			userMouseFristMove: true,
			userRated: false,
			userLastRate: 1,
		}
	},
	watch: {
		value(newVal) {
			this.valueSync = Number(newVal)
		},
		modelValue(newVal) {
			this.valueSync = Number(newVal)
		},
	},
	computed: {
		stars() {
			const value = this.valueSync ? this.valueSync : 0
			const starList = []
			const floorValue = Math.floor(value)
			const ceilValue = Math.ceil(value)
			for (let i = 0; i < this.max; i++) {
				if (floorValue > i) {
					starList.push({
						activeWitch: '100%',
					})
				} else if (ceilValue - 1 === i) {
					starList.push({
						activeWitch: (value - floorValue) * 100 + '%',
					})
				} else {
					starList.push({
						activeWitch: '0',
					})
				}
			}
			return starList
		},

		marginNumber() {
			return Number(this.margin)
		},
	},
	created() {
		this.valueSync = Number(this.value || this.modelValue)
		this._rateBoxLeft = 0
		this._oldValue = null
	},
	mounted() {
		setTimeout(() => {
			this._getSize()
		}, 100)
		// #ifdef H5
		this.PC = this.IsPC()
		// #endif
	},
	methods: {
		touchstart(e) {
			// #ifdef H5
			if (this.IsPC()) return
			// #endif
			if (this.readonly || this.disabled) return
			const { clientX, screenX } = e.changedTouches[0]
			// TODO 做一下兼容，只有 Nvue 下才有 screenX，其他平台式 clientX
			this._getRateCount(clientX || screenX)
		},
		touchmove(e) {
			// #ifdef H5
			if (this.IsPC()) return
			// #endif
			if (this.readonly || this.disabled || !this.touchable) return
			const { clientX, screenX } = e.changedTouches[0]
			this._getRateCount(clientX || screenX)
		},

		/**
		 * 兼容 PC @tian
		 */

		mousedown(e) {
			// #ifdef H5
			if (!this.IsPC()) return
			if (this.readonly || this.disabled) return
			const { clientX } = e
			this.userLastRate = this.valueSync
			this._getRateCount(clientX)
			this.userRated = true
			// #endif
		},
		mousemove(e) {
			// #ifdef H5
			if (!this.IsPC()) return
			if (this.userRated) return
			if (this.userMouseFristMove) {
				console.log('---mousemove----', this.valueSync)
				this.userLastRate = this.valueSync
				this.userMouseFristMove = false
			}
			if (this.readonly || this.disabled || !this.touchable) return
			const { clientX } = e
			this._getRateCount(clientX)
			// #endif
		},
		mouseleave(e) {
			// #ifdef H5
			if (!this.IsPC()) return
			if (this.readonly || this.disabled || !this.touchable) return
			if (this.userRated) {
				this.userRated = false
				return
			}
			this.valueSync = this.userLastRate
			// #endif
		},
		// #ifdef H5
		IsPC() {
			var userAgentInfo = navigator.userAgent
			var Agents = [
				'Android',
				'iPhone',
				'SymbianOS',
				'Windows Phone',
				'iPad',
				'iPod',
			]
			var flag = true
			for (let v = 0; v < Agents.length - 1; v++) {
				if (userAgentInfo.indexOf(Agents[v]) > 0) {
					flag = false
					break
				}
			}
			return flag
		},
		// #endif

		/**
		 * 获取星星个数
		 */
		_getRateCount(clientX) {
			this._getSize()
			const size = Number(this.size)
			if (isNaN(size)) {
				return new Error('size 属性只能设置为数字')
			}
			const rateMoveRange = clientX - this._rateBoxLeft
			let index = parseInt(rateMoveRange / (size + this.marginNumber))
			index = index < 0 ? 0 : index
			index = index > this.max ? this.max : index
			const range = parseInt(
				rateMoveRange - (size + this.marginNumber) * index
			)
			let value = 0
			if (this._oldValue === index && !this.PC) return
			this._oldValue = index
			if (this.allowHalf) {
				if (range > size / 2) {
					value = index + 1
				} else {
					value = index + 0.5
				}
			} else {
				value = index + 1
			}

			value = Math.max(0.5, Math.min(value, this.max))
			this.valueSync = value
			this._onChange()
		},

		/**
		 * 触发动态修改
		 */
		_onChange() {
			this.$emit('input', this.valueSync)
			this.$emit('update:modelValue', this.valueSync)
			this.$emit('change', {
				value: this.valueSync,
			})
		},
		/**
		 * 获取星星距离屏幕左侧距离
		 */
		_getSize() {
			// #ifndef APP-NVUE
			uni.createSelectorQuery()
				.in(this)
				.select('.uni-rate')
				.boundingClientRect()
				.exec((ret) => {
					if (ret) {
						this._rateBoxLeft = ret[0].left
					}
				})
			// #endif
			// #ifdef APP-NVUE
			dom.getComponentRect(this.$refs['uni-rate'], (ret) => {
				const size = ret.size
				if (size) {
					this._rateBoxLeft = size.left
				}
			})
			// #endif
		},
	},
}
</script>

<style lang="scss">
.uni-rate {
	/* #ifndef APP-NVUE */
	display: flex;
	/* #endif */
	line-height: 1;
	font-size: 0;
	flex-direction: row;
	/* #ifdef H5 */
	cursor: pointer;
	/* #endif */
}

.uni-rate__icon {
	position: relative;
	line-height: 1;
	font-size: 0;
}

.uni-rate__icon-on {
	overflow: hidden;
	position: absolute;
	top: 0;
	left: 0;
	line-height: 1;
	text-align: left;
}

.uni-cursor-not-allowed {
	/* #ifdef H5 */
	cursor: not-allowed !important;
	/* #endif */
}
</style>
